﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.PowerShell;
using System.Management.Automation;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Navigation;

namespace SlamCms.SharePoint.PowerShell
{
	[Cmdlet(VerbsCommon.New, "PublishingSite")]
	public class SpCmdletNewPublishingSite : SPCmdlet
	{
		#region Properties
		[Parameter(Mandatory = true)]
		public string ParentSiteUrl
		{
			get;
			set;
		}

		[Parameter(Mandatory = true)]
		public string NewSiteName
		{
			get;
			set;
		}

		[Parameter(Mandatory = true)]
		public string NewSiteUrl
		{
			get;
			set;
		}

		[Parameter(Mandatory = true)]
		public string GroupOwner
		{
			get;
			set;
		}

		[Parameter(Mandatory = true)]
		public string WebTemplateName
		{
			get;
			set;
		}

		[Parameter(Mandatory = false)]
		public string PublisherGroupName
		{
			get;
			set;
		}

		[Parameter(Mandatory = false)]
		public string EditorGroupName
		{
			get;
			set;
		}

		[Parameter(Mandatory = false)]
		public string EditorRoleName
		{
			get;
			set;
		}

		[Parameter(Mandatory = false)]
		public string PublisherRoleName
		{
			get;
			set;
		}

		#endregion


		#region Constants

		private string DefaultEditorGroupName
		{
			get
			{
				return "Editors";
			}
		}

		private string DefaultPublisherGroupName
		{
			get
			{
				return "Publishers";
			}
		}

		private string DefaultPublisherRoleName
		{
			get
			{
				return "Publish";
			}
		}

		private string ContributeRoleName
		{
			get
			{
				return "Contribute";
			}
		}

		#endregion


		protected override void InternalProcessRecord()
		{
			using (SPSite siteCollection = new SPSite(ParentSiteUrl))
			{
				using (SPWeb parentSite = siteCollection.OpenWeb())
				{	
					SPWebTemplate template = null;
					try
					{
						template = siteCollection.GetWebTemplates(parentSite.Language)[WebTemplateName];
					}
					catch (Exception ex)
					{
						WriteObject("Web Template " + WebTemplateName + " can not be found.");
						throw ex;
					}
					try
					{
						//Site is created
						var existingSite = parentSite.Webs[NewSiteUrl];
						if (existingSite == null || !existingSite.Exists)
						{
							var newSite = parentSite.Webs.Add(NewSiteUrl, NewSiteName, NewSiteName, parentSite.Language, template, true, false);
							newSite.Navigation.UseShared = true;

							SPNavigationNode newSiteNode = new SPNavigationNode(NewSiteName, NewSiteUrl);
							foreach (SPNavigationNode node in parentSite.Navigation.QuickLaunch)
							{
								if (node.Title == "Sites")
								{
									node.Children.AddAsLast(newSiteNode);
									break;
								}
							}
							parentSite.Update();

							// Create Groups and Roles
							newSite.AllowUnsafeUpdates = true;
							SetPermissions(newSite);
							newSite.AllowUnsafeUpdates = false;

							newSite.Dispose();
						}

						if (existingSite != null && existingSite.Exists)
							existingSite.Dispose();
					}
					catch (Exception ex)
					{
						WriteObject("There was an error during the process.");
						WriteObject("Exception: " + ex.Message + " Stack: " + ex.StackTrace);
					}
				}
			}
		}

		private void SetPermissions(SPWeb web)
		{
			// Breaking inheritance
			web.BreakRoleInheritance(true);
			web.RoleDefinitions.BreakInheritance(true, false);
			web.Update();

			SPUser adminUser = web.EnsureUser(GroupOwner);

			//Add Editor group
			string editorGroupName = DefaultEditorGroupName;
			if (!String.IsNullOrEmpty(EditorGroupName))
				editorGroupName = EditorGroupName;

			editorGroupName = String.Format("{0} {1}", NewSiteName, editorGroupName);
			try
			{
				var group = web.SiteGroups[editorGroupName];
			}
			catch (SPException ex)
			{
				if (ex.Message.Contains("Group cannot be found"))
					web.SiteGroups.Add(editorGroupName, adminUser, adminUser, editorGroupName);
				else
					throw ex;
			}

			//Add Publisher group
			string publisherGroupName = DefaultPublisherGroupName;
			if (!String.IsNullOrEmpty(PublisherGroupName))
				publisherGroupName = PublisherGroupName;

			publisherGroupName = String.Format("{0} {1}", NewSiteName, publisherGroupName);
			try
			{
				var group = web.SiteGroups[publisherGroupName];
			}
			catch (SPException ex)
			{
				if (ex.Message.Contains("Group cannot be found"))
					web.SiteGroups.Add(publisherGroupName, adminUser, adminUser, publisherGroupName);
				else
					throw ex;
			}

			//Add Global Publishers site group
			string globalSiteGroupName = "Global " + DefaultPublisherGroupName;
			try
			{
				var group = web.SiteGroups[globalSiteGroupName];
			}
			catch (SPException ex)
			{
				if (ex.Message.Contains("Group cannot be found"))
					web.SiteGroups.Add(globalSiteGroupName, adminUser, adminUser, globalSiteGroupName);
				else
					throw ex;
			}

			string publishRoleName = DefaultPublisherRoleName;
			if (!String.IsNullOrEmpty(PublisherRoleName))
				publishRoleName = PublisherRoleName;

			SPRoleDefinition publishRoleDefinition = web.RoleDefinitions.Cast<SPRoleDefinition>().Where(rd => rd.Name == publishRoleName).FirstOrDefault();
			if (publishRoleDefinition == null)
			{
				publishRoleDefinition = new SPRoleDefinition(web.RoleDefinitions[ContributeRoleName]);
				publishRoleDefinition.Name = publishRoleName;
				publishRoleDefinition.Description = publishRoleDefinition.Description.Replace("Can", "Can approve,");
				publishRoleDefinition.BasePermissions = publishRoleDefinition.BasePermissions | SPBasePermissions.ApproveItems;

				web.RoleDefinitions.Add(publishRoleDefinition);
				web.Update();
			}

			//The role must be retrieve to be used for the assignment
			publishRoleDefinition = web.RoleDefinitions.Cast<SPRoleDefinition>().FirstOrDefault(def => def.Name.Equals(publishRoleName, StringComparison.OrdinalIgnoreCase));

			SPRoleAssignment publishRoleAssignment = new SPRoleAssignment(web.SiteGroups[publisherGroupName]);
			publishRoleAssignment.RoleDefinitionBindings.Add(publishRoleDefinition);
			web.RoleAssignments.Add(publishRoleAssignment);

			SPRoleAssignment editRoleAssignment = new SPRoleAssignment(web.SiteGroups[editorGroupName]);
			editRoleAssignment.RoleDefinitionBindings.Add(web.RoleDefinitions[ContributeRoleName]);
			web.RoleAssignments.Add(editRoleAssignment);

			SPRoleAssignment globalRoleAssignment = new SPRoleAssignment(web.SiteGroups[globalSiteGroupName]);
			globalRoleAssignment.RoleDefinitionBindings.Add(publishRoleDefinition);
			web.RoleAssignments.Add(globalRoleAssignment);
			web.Update();
		}
	}
}
